//	VirtualDub - Video processing and capture application
//	Copyright (C) 1998-2001 Avery Lee
//
//	This program is free software; you can redistribute it and/or modify
//	it under the terms of the GNU General Public License as published by
//	the Free Software Foundation; either version 2 of the License, or
//	(at your option) any later version.
//
//	This program is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//	GNU General Public License for more details.
//
//	You should have received a copy of the GNU General Public License
//	along with this program; if not, write to the Free Software
//	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

#include <new>
#include "VBitmap.h"
#include "cpuaccel.h"
#include "resample.h"

#define FP_EPSILON	(1e-30)

extern "C" void __cdecl asm_resize_nearest
						(
							Pixel32			*dst,
							Pixel32			*src,
							long			width,
							PixDim			height,
							PixOffset		dstpitch,
							PixOffset		srcpitch,
							unsigned long	xaccum,
							unsigned long	yaccum,
							unsigned long	xfrac,
							unsigned long	yfrac,
							long			xistep,
							PixOffset		yistep,
							Pixel32			*precopysrc,
							unsigned long	precopy,
							Pixel32			*postcopysrc,
							unsigned long	postcopy
						);

extern "C" void __cdecl asm_resize_bilinear
						(
							void			*dst,
							void			*src,
							long			w,
							PixDim			h,
							PixOffset		dstpitch,
							PixOffset		srcpitch,
							unsigned long	xaccum,
							unsigned long	yaccum,
							unsigned long	xfrac,
							unsigned long	yfrac,
							long			xistep,
							PixOffset		yistep,
							Pixel32			*precopysrc,
							unsigned long	precopy,
							Pixel32			*postcopysrc,
							unsigned long	postcopy
						);

extern "C" void __cdecl asm_bitmap_xlat1
						(
							Pixel32			*dst,
							Pixel32			*src,
							PixOffset		dpitch,
							PixOffset		spitch,
							PixDim			w,
							PixDim			h,
							const Pixel8	*tbl
						);

extern "C" void __cdecl asm_bitmap_xlat3
						(
							Pixel32			*dst,
							Pixel32			*src,
							PixOffset		dpitch,
							PixOffset		spitch,
							PixDim			w,
							PixDim			h,
							const Pixel32	*tbl
						);

/*
 =======================================================================================================================
 =======================================================================================================================
 */
VBitmap::VBitmap(void *lpData, BITMAPINFOHEADER *bmih)
{
	init(lpData, bmih);
}

/*
 =======================================================================================================================
 =======================================================================================================================
 */
VBitmap::VBitmap(void *data, PixDim w, PixDim h, int depth)
{
	init(data, w, h, depth);
}

/*
 =======================================================================================================================
 =======================================================================================================================
 */
VBitmap &VBitmap::init(void *lpData, BITMAPINFOHEADER *bmih)
{
	data = (Pixel *) lpData;
	palette = (Pixel *) (bmih + 1);
	depth = bmih->biBitCount;
	w = bmih->biWidth;
	h = bmih->biHeight;
	offset = 0;
	AlignTo4();

	return *this;
}

/*
 =======================================================================================================================
 =======================================================================================================================
 */
VBitmap &VBitmap::init(void *data, PixDim w, PixDim h, int depth)
{
	this->data = (Pixel32 *) data;
	this->palette = NULL;
	this->depth = depth;
	this->w = w;
	this->h = h;
	this->offset = 0;
	AlignTo4();

	return *this;
}

/*
 =======================================================================================================================
 =======================================================================================================================
 */
void VBitmap::MakeBitmapHeader(BITMAPINFOHEADER *bih) const
{
	bih->biSize = sizeof(BITMAPINFOHEADER);
	bih->biBitCount = depth;
	bih->biPlanes = 1;
	bih->biCompression = BI_RGB;

	if(pitch == ((w * bih->biBitCount + 31) / 32) * 4)
		bih->biWidth = w;
	else
		bih->biWidth = pitch * 8 / depth;

	bih->biHeight = h;
	bih->biSizeImage = pitch * h;
	bih->biClrUsed = 0;
	bih->biClrImportant = 0;
	bih->biXPelsPerMeter = 0;
	bih->biYPelsPerMeter = 0;
}

/*
 =======================================================================================================================
 =======================================================================================================================
 */
void VBitmap::MakeBitmapHeaderNoPadding(BITMAPINFOHEADER *bih) const
{
	bih->biSize = sizeof(BITMAPINFOHEADER);
	bih->biBitCount = depth;
	bih->biPlanes = 1;
	bih->biCompression = BI_RGB;
	bih->biWidth = w;
	bih->biHeight = h;
	bih->biSizeImage = (((w * bih->biBitCount + 31) / 32) * 4) * h;
	bih->biClrUsed = 0;
	bih->biClrImportant = 0;
	bih->biXPelsPerMeter = 0;
	bih->biYPelsPerMeter = 0;
}

/*
 =======================================================================================================================
 =======================================================================================================================
 */
void VBitmap::AlignTo4()
{
	pitch = PitchAlign4();
	modulo = Modulo();
	size = Size();
}

/*
 =======================================================================================================================
 =======================================================================================================================
 */
void VBitmap::AlignTo8()
{
	pitch = PitchAlign8();
	modulo = Modulo();
	size = Size();
}

/*
 =======================================================================================================================
 =======================================================================================================================
 */
bool VBitmap::dualrectclip
(
	PixCoord		&x2,
	PixCoord		&y2,
	const VBitmap	*src,
	PixCoord		&x1,
	PixCoord		&y1,
	PixDim			&dx,
	PixDim			&dy
) const
{
	if(dx == -1) dx = src->w;
	if(dy == -1) dy = src->h;

	/* clip to source bitmap */
	if(x1 < 0)
	{
		dx += x1;
		x2 -= x1;
		x1 = 0;
	}

	if(y1 < 0)
	{
		dy += y1;
		y2 -= y1;
		y1 = 0;
	}

	if(x1 + dx > src->w) dx = src->w - x1;
	if(y1 + dy > src->h) dy = src->h - y1;

	/* clip to destination bitmap */
	if(x2 < 0)
	{
		dx += x2;
		x1 -= x2;
		x2 = 0;
	}

	if(y2 < 0)
	{
		dy += y2;
		y1 -= y2;
		y2 = 0;
	}

	if(x2 + dx > w) dx = w - x2;
	if(y2 + dy > h) dy = h - y2;

	/* anything left to blit? */
	if(dx <= 0 || dy <= 0) return false;

	return true;
}

/*
 =======================================================================================================================
 =======================================================================================================================
 */
bool VBitmap::BitBltXlat1
(
	PixCoord		x2,
	PixCoord		y2,
	const VBitmap	*src,
	PixCoord		x1,
	PixCoord		y1,
	PixDim			dx,
	PixDim			dy,
	const Pixel8	*tbl
) const
{
	if(depth != 32) return false;
	if(!dualrectclip(x2, y2, src, x1, y1, dx, dy)) return false;

	/* do the translate */
	asm_bitmap_xlat1
	(
		this->Address32(x2, y2 + dy - 1) + dx,
		src->Address32(x1, y1 + dy - 1) + dx,
		this->pitch,
		src->pitch,
		-4 * dx,
		dy,
		tbl
	);

	return true;
}

/*
 =======================================================================================================================
 =======================================================================================================================
 */
bool VBitmap::BitBltXlat3
(
	PixCoord		x2,
	PixCoord		y2,
	const VBitmap	*src,
	PixCoord		x1,
	PixCoord		y1,
	PixDim			dx,
	PixDim			dy,
	const Pixel32	*tbl
) const
{
	if(depth != 32) return false;
	if(!dualrectclip(x2, y2, src, x1, y1, dx, dy)) return false;

	/* do the translate */
	asm_bitmap_xlat3
	(
		this->Address32(x2, y2 + dy - 1) + dx,
		src->Address32(x1, y1 + dy - 1) + dx,
		this->pitch,
		src->pitch,
		-4 * dx,
		dy,
		tbl
	);

	return true;
}

/*
 =======================================================================================================================
 =======================================================================================================================
 */
bool VBitmap::StretchBltNearestFast
(
	PixCoord		x2,
	PixCoord		y2,
	PixDim			dx,
	PixDim			dy,
	const VBitmap	*src,
	double			x1,
	double			y1,
	double			dx1,
	double			dy1
) const
{
	/* No format conversions!! */
	if(src->depth != depth) return false;

	/* Right now, only do 32-bit stretch. (24-bit is a pain, 16-bit is slow.) */
	if(depth != 32) return false;

	/*~~~~~~~~~~~~~~~~~~~~~~~~*/
	/* Compute clipping parameters. */
	ResampleInfo	horiz, vert;
	/*~~~~~~~~~~~~~~~~~~~~~~~~*/

	if(!horiz.init(x2, dx, x1 + 0.5, dx1, w, src->w, 1, false, false)) return false;
	if(!vert.init(y2, dy, y1 + 0.5, dy1, h, src->h, 1, false, false)) return false;

	/* Call texturing routine. */
	if(vert.clip.precopy)
	{
		asm_resize_nearest
		(
			Address32(horiz.x1_int + horiz.clip.precopy + horiz.clip.unclipped, vert.x1_int),	/* destination pointer,
																								 * right side */
			src->Address32(horiz.u0_int.hi, 0),
			-horiz.clip.unclipped * 4,	/* -width*4 */
			vert.clip.precopy,			/* height */
			-pitch,						/* dstpitch */
			0,						/* srcpitch */
			horiz.u0_int.lo,		/* xaccum */
			0,						/* yaccum */
			horiz.dudx_int.lo,		/* xfrac */
			0,						/* yfrac */
			horiz.dudx_int.hi,		/* xinc */
			0,						/* yinc */
			src->Address32(0, 0),	/* precopysrc */
			horiz.clip.precopy,		/* precopy */
			src->Address32(src->w - 1, 0),	/* postcopysrc */
			horiz.clip.postcopy				/* postcopy */
		);
	}

	asm_resize_nearest
	(
		Address32(horiz.x1_int + horiz.clip.precopy + horiz.clip.unclipped, vert.x1_int + vert.clip.precopy),	/* destination
																												 * pointer,
																												 * right
																												 * side
																												 * */
		src->Address32(horiz.u0_int.hi, vert.u0_int.hi),
		-horiz.clip.unclipped * 4,		/* -width*4 */
		vert.clip.unclipped,			/* height */
		-pitch,							/* dstpitch */
		-src->pitch,					/* srcpitch */
		horiz.u0_int.lo,				/* xaccum */
		vert.u0_int.lo,					/* yaccum */
		horiz.dudx_int.lo,				/* xfrac */
		vert.dudx_int.lo,				/* yfrac */
		horiz.dudx_int.hi,				/* xinc */
		-vert.dudx_int.hi * src->pitch, /* yinc */
		src->Address32(0, vert.u0_int.hi),
		horiz.clip.precopy,				/* precopy */
		src->Address32(src->w - 1, vert.u0_int.hi),
		horiz.clip.postcopy				/* postcopy */
	);

	if(vert.clip.postcopy)
	{
		asm_resize_nearest
		(
			Address32
				(
					horiz.x1_int + horiz.clip.precopy + horiz.clip.unclipped,
					vert.x1_int + vert.clip.precopy + vert.clip.unclipped
				),						/* destination pointer, right side */
			src->Address32(horiz.u0_int.hi, src->h - 1),
			-horiz.clip.unclipped * 4,	/* -width*4 */
			vert.clip.postcopy,			/* height */
			-pitch,						/* dstpitch */
			0,					/* srcpitch */
			horiz.u0_int.lo,	/* xaccum */
			0,					/* yaccum */
			horiz.dudx_int.lo,	/* xfrac */
			0,					/* yfrac */
			horiz.dudx_int.hi,	/* xinc */
			0,					/* yinc */
			src->Address32(0, src->h - 1),
			horiz.clip.precopy, /* precopy */
			src->Address32(src->w - 1, src->h - 1),
			horiz.clip.postcopy
		);						/* postcopy */
	}

	return true;
}

/*
 =======================================================================================================================
 =======================================================================================================================
 */
bool VBitmap::StretchBltBilinearFast
(
	PixCoord		x2,
	PixCoord		y2,
	PixDim			dx,
	PixDim			dy,
	const VBitmap	*src,
	double			x1,
	double			y1,
	double			dx1,
	double			dy1
) const
{
	/* No format conversions!! */
	if(src->depth != depth) return false;

	/* Right now, only do 32-bit stretch. (24-bit is a pain, 16-bit is slow.) */
	if(depth != 32) return false;

	/*~~~~~~~~~~~~~~~~~~~~~~~~*/
	/* Compute clipping parameters. */
	ResampleInfo	horiz, vert;
	/*~~~~~~~~~~~~~~~~~~~~~~~~*/

	if(!horiz.init(x2, dx, x1 + (1.0 / 32.0), dx1, w, src->w, 2, false, false)) return false;
	if(!vert.init(y2, dy, y1 + (1.0 / 32.0), dy1, h, src->h, 2, false, false)) return false;

	/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
	/* Call texturing routine. */
	int xprecopy = horiz.clip.preclip + horiz.clip.precopy;
	int xpostcopy = horiz.clip.postclip + horiz.clip.postcopy;
	int yprecopy = vert.clip.preclip + vert.clip.precopy;
	int ypostcopy = vert.clip.postclip + vert.clip.postcopy;
	/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

	if(yprecopy)
	{
		asm_resize_bilinear
		(
			Address32(horiz.x1_int + xprecopy + horiz.clip.unclipped, vert.x1_int), /* destination pointer, right side */
			src->Address32(horiz.u0_int.hi, 0),
			-horiz.clip.unclipped * 4,	/* -width*4 */
			yprecopy,					/* height */
			-pitch,						/* dstpitch */
			0,						/* srcpitch */
			horiz.u0_int.lo,		/* xaccum */
			0,						/* yaccum */
			horiz.dudx_int.lo,		/* xfrac */
			0,						/* yfrac */
			horiz.dudx_int.hi,		/* xinc */
			0,						/* yinc */
			src->Address32(0, 0),	/* precopysrc */
			-xprecopy * 4,			/* precopy */
			src->Address32(src->w - 1, 0),	/* postcopysrc */
			-xpostcopy * 4					/* postcopy */
		);
	}

	asm_resize_bilinear
	(
		Address32(horiz.x1_int + xprecopy + horiz.clip.unclipped, vert.x1_int + yprecopy),	/* destination pointer,
																							 * right side */
		src->Address32(horiz.u0_int.hi, vert.u0_int.hi),
		-horiz.clip.unclipped * 4,		/* -width*4 */
		vert.clip.unclipped,			/* height */
		-pitch,							/* dstpitch */
		-src->pitch,					/* srcpitch */
		horiz.u0_int.lo,				/* xaccum */
		vert.u0_int.lo,					/* yaccum */
		horiz.dudx_int.lo,				/* xfrac */
		vert.dudx_int.lo,				/* yfrac */
		horiz.dudx_int.hi,				/* xinc */
		-vert.dudx_int.hi * src->pitch, /* yinc */
		src->Address32(0, vert.u0_int.hi),
		-xprecopy * 4,					/* precopy */
		src->Address32(src->w - 1, vert.u0_int.hi),
		-xpostcopy * 4					/* postcopy */
	);

	if(ypostcopy)
	{
		asm_resize_bilinear
		(
			Address32(horiz.x1_int + xprecopy + horiz.clip.unclipped, vert.x1_int + yprecopy + vert.clip.unclipped),	/* destination
																														 * pointer,
																														 * right
																														 * side
																														 * */
			src->Address32(horiz.u0_int.hi, src->h - 1),
			-horiz.clip.unclipped * 4,	/* -width*4 */
			ypostcopy,					/* height */
			-pitch,						/* dstpitch */
			0,					/* srcpitch */
			horiz.u0_int.lo,	/* xaccum */
			0,					/* yaccum */
			horiz.dudx_int.lo,	/* xfrac */
			0,					/* yfrac */
			horiz.dudx_int.hi,	/* xinc */
			0,					/* yinc */
			src->Address32(0, src->h - 1),
			-xprecopy * 4,		/* precopy */
			src->Address32(src->w - 1, src->h - 1),
			-xpostcopy * 4
		);						/* postcopy */
	}

	return true;
}

/*
 =======================================================================================================================
 =======================================================================================================================
 */
bool VBitmap::RectFill(PixCoord x, PixCoord y, PixDim dx, PixDim dy, Pixel32 c) const
{
	if(depth != 32) return false;

	/*~~~~~~~~~~*/
	/* Do the blit */
	Pixel32 *dstp;
	/*~~~~~~~~~~*/

	if(dx == -1) dx = w;
	if(dy == -1) dy = h;

	/* clip to destination bitmap */
	if(x < 0)
	{
		dx += x;
		x = 0;
	}

	if(y < 0)
	{
		dy += y;
		y = 0;
	}

	if(x + dx > w) dx = w - x;
	if(y + dy > h) dy = h - y;

	/* anything left to fill? */
	if(dx <= 0 || dy <= 0) return false;

	/* compute coordinates */
	dstp = Address32(x, y + dy - 1);

	/* do the fill */
	do
	{
		/*~~~~~~~~~~~~~~~~~*/
		PixDim	dxt = dx;
		Pixel32 *dst2 = dstp;
		/*~~~~~~~~~~~~~~~~~*/

		do
		{
			*dst2++ = c;
		} while(--dxt);

		dstp = (Pixel32 *) ((char *) dstp + pitch);
	} while(--dy);

	return true;
}
